package com.netflexitysolutions.amazonws.sdb.orm.internal.operations;

import java.util.List;
import java.util.concurrent.Callable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.netflexitysolutions.amazonws.sdb.Domain;
import com.netflexitysolutions.amazonws.sdb.Item;
import com.netflexitysolutions.amazonws.sdb.orm.SimpleDBSession;
import com.netflexitysolutions.amazonws.sdb.orm.SimpleDBSessionListener;
import com.netflexitysolutions.amazonws.sdb.orm.metadata.ItemMetadata;

public class Find<T> implements Callable<T> {

	private static Log logger = LogFactory.getLog(Find.class.getName());
	
	private SimpleDBSession session;
	private Class<T> clazz;
	private String id;

	/**
	 * @param session
	 * @param clazz
	 * @param id
	 */
	public Find(SimpleDBSession session, Class<T> clazz, String id) {
		this.session = session;
		this.clazz = clazz;
		this.id = id;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.concurrent.Callable#call()
	 */
	public T call() throws Exception {
		// Obtain metadata for this record type.
        ItemMetadata itemMetadata = session.getMetadata(clazz);
        assert(itemMetadata != null);
        
		// Invoke before select.
		List<SimpleDBSessionListener> listeners = session.getFactory().getEntityInterceptors();
		for (SimpleDBSessionListener simpleDBSessionListener : listeners) {
			simpleDBSessionListener.beforeSelect(itemMetadata);
		}
		
		T cachedObject = session.cacheGet(clazz, id);
        if (cachedObject != null) {
            if(logger.isDebugEnabled()){
                logger.debug("Found in cache: " + cachedObject);
            }
            return cachedObject;
        }
        
		// Obtain domain.
        Domain domain = session.getDomain(clazz);
        assert(domain != null);
        
        // Create appropriate Amazon SDB attributes.
        SimpleDBItemMapper<T> mapper = new SimpleDBItemMapper<T>(session, itemMetadata);
        
        // Get from Amazon SimpleDB.
        Item item = new Item();
        item.setIdentifier(id);
        item = session.getFactory().getDataSource().getItem(domain, item);
        if (item == null) return null;
        T record = mapper.mapItem(item);
        
        // Cache record only if not cachable type. 
        // NOTE: Cachable types will get pre-loaded through FindAll anyway.
        if(!itemMetadata.isCachable()){
        	session.cachePut(record);
        }
        
        return record;
	}

	/**
	 * @return the session
	 */
	public SimpleDBSession getSession() {
		return session;
	}

	/**
	 * @param session the session to set
	 */
	public void setSession(SimpleDBSession session) {
		this.session = session;
	}

	/**
	 * @return the clazz
	 */
	public Class<T> getClazz() {
		return clazz;
	}

	/**
	 * @param clazz the clazz to set
	 */
	public void setClazz(Class<T> clazz) {
		this.clazz = clazz;
	}

	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}
}
